#/usr/bin/python27
#-*- coding: utf-8 -*-
#  Copyright 2011-2013 wuweihui@cn.ibm.com
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

#from Telnet import *
from CIMLogParser import *
from SSHClient import *
from config import *
from CLILogParser import *

def init_config():
   global MSClient
   global CIMAgent
   global CIM_SRV_IS_SET
   MSClient = None  
   CIMAgent = None 
   CIM_SRV_IS_SET = False
   
init_config()

def connect_to_win(timeout=120.0):
   global MSClient
   global CIM_SRV_IS_SET
   if MSClient:
      try:
         MSClient.close_connection()
      except:
         pass
   if config.get('MS SMS', 'protocol').lower() == 'ssh':
       MSClient = SSHWIN(config.get('MS SMS', 'server'), config.getint('MS SMS', 'port'), timeout, newline='\r')
   else:
       MSClient = WinTelnet(config.get('MS SMS', 'server'), config.getint('MS SMS', 'port'), timeout, newline='\r\n')
   MSClient.set_prompt(re.compile('[A-Za-z]\:\\\\[\w \\\\\-\.]*>'), 60)
   MSClient.login(config.get('MS SMS', 'user'), config.get('MS SMS', 'passwd'))
   MSClient.set_timeout(eval(config.get('Common', 'timeout')))
   CIM_SRV_IS_SET = False
   
def connect_to_cimagent(timeout=120, envsetup=True):
   global CIMAgent
   if CIMAgent:
      try:
         CIMAgent.close_connection()
      except:
         pass
   if config.get('CIM Agent', 'protocol').lower() == 'ssh':
       CIMAgent = SSHCIMAgent(config.get('CIM Agent', 'server'), config.getint('CIM Agent', 'port'), timeout, newline='\n')
       if not config.get('CIM Agent', 'keyfile'):
           CIMAgent.login(config.get('CIM Agent', 'user'), config.get('CIM Agent', 'passwd'))
       else:
           CIMAgent.login_with_public_key(config.get('CIM Agent', 'user'), config.get('CIM Agent', 'keyfile'), config.get('CIM Agent', 'passwd'))
   else:
       CIMAgent = WinTelnet(config.get('CIM Agent', 'server'), config.getint('CIM Agent', 'port'), timeout, newline='\r\n')
       CIMAgent.login(config.get('CIM Agent', 'user'), config.get('CIM Agent', 'passwd'))
   CIMAgent.set_prompt(re.compile(config.get('PROMPT', config.get('CIM Agent', 'servertype'))+' '), 50)
   if envsetup:
      envcmd = config.get('CIM Agent', 'servertype') + '_env_setup()'
      eval(envcmd)
   CIMAgent.set_timeout(eval(config.get('Common', 'timeout')))

#def start_interapp(path=config.get('MS SMS', 'interapp')):
#   exec_ms_cmd('dir')
#   MSClient.write('cd "' + path + '"')
#   MSClient.read_until_prompt()
#   MSClient.set_prompt('$ ', 2)
#   MSClient.write('InteractiveApp.exe')
#   MSClient.read_until_prompt()

def close_all_connections():
    global CIM_SRV_IS_SET
    for con in [MSClient, CIMAgent]:
        try:
            con.close_connection()
        except:
            pass
    CIM_SRV_IS_SET = False

def exec_cmd(client, cmd, parser=None):
   client.write(cmd)
   out = client.read_until_prompt()
   #out = client.execute_command(cmd)
   if parser:
      return parser(out)
   else:
      return out

def exec_ms_cmd(cmd, parser=None):
   return exec_cmd(MSClient, cmd, parser)

def exec_ms_cmd_ex(*args, **kws):
   cmdargs = []
   for arg in args:
      if not isinstance(arg, basestring):
         arg = arg.ObjectId
      cmdargs.append(arg)
   if kws.has_key('acc') and kws['acc'] == False:
      cmd = _build_cmd(*cmdargs)
   else:
      cmd = _build_cmd_with_acc(*cmdargs)
   if kws.has_key('parser'):
      parser = eval(kws['parser'])
   else:
      parser = cMSLogItem
   return exec_sms_cmd(cmd, parser) 
   

def exec_sms_cmd(cmd, parser=None):
   if not MSClient:
       connect_to_win()
   if not MSClient.appmod:
       MSClient.start_interapp(config.get('MS SMS', 'interapp'))
   if cmd in ["deleteReplicaList", "deleteStorageVolumeList"]:
      oldprompt = MSClient.set_prompt(re.compile('number to remove: '), 20)
      out = ''
      try:
         out = exec_cmd(MSClient, cmd, parser)
      finally:
         MSClient.set_prompt(*oldprompt)
      return out
   return exec_cmd(MSClient, cmd, parser)
   
def exec_cim_cmd(cmd, parser=None):
   if not CIMAgent:
      connect_to_cimagent()
   return exec_cmd(CIMAgent, cmd, parser)

def start_discovery(host=None, protocol='smis-cimxml', level=None, force=None, parser=cMSDiscoverItem):
   """
   sd or startDiscovery <host system><host-resource|smis-wmi|smis-cimxml> [[user name] [pwd|!]]
   level = 0, 1, 2
   force = True, False
   """
   host = host or config.get('CIM Agent', 'server') + ':' + config.get('CIM Agent', 'srvport')
   if level != None:
      exec_sms_cmd(_build_cmd('discoveryLevel', level))
   if force != None:
      exec_sms_cmd(_build_cmd('forcerediscovery', force and 'y' or 'n'))
   return exec_sms_cmd(_build_cmd_with_acc('startDiscovery', host, protocol), parser)
   pass

def wait_until_discovery_finish(timeout=120, parser=cMSDiscoverStatusItem):
   oldprompt = MSClient.set_prompt(re.compile('discovery \{[\w\-]+\} \w+\; status \[\d+/.*\]', re.DOTALL), 100)
   oldtimeout = MSClient.set_timeout(timeout)
   out = ''
   try:
      out = MSClient.read_until_prompt()
   finally:
      MSClient.set_prompt(*oldprompt)
      MSClient.set_timeout(oldtimeout)
   if out and parser:
      return parser(out)
   return out

def ms_enumerate(classname, filter='', parser=eMSLogItemList):   
   """
   e or enumerate [class name]
   ef or enumerateFilter <class name><string>
   """
   if not filter:
      return exec_sms_cmd(_build_cmd('enumerate', classname), parser)
   else:
      return exec_sms_cmd(_build_cmd('enumerateFilter', classname, filter.lower()!='all' and filter or ''), parser)

def set_cim_server(server=None, namespace=None, user=None, passwd=None):
   """
   srv 9.186.96.201:5989 root/ibm admin adminadmin
   """
   global CIM_SRV_IS_SET
   
   server = server or config.get('CIM Agent', 'server') + ':' + config.get('CIM Agent', 'srvport')
   namespace = namespace or config.get('CIM Agent', 'namespace')
   user = user or config.get('CLI server', 'user')
   passwd = passwd or config.get('CLI server', 'passwd')
   out = exec_sms_cmd(_build_cmd('srv', server, namespace, user, passwd), parser=None)
   CIM_SRV_IS_SET = True
   return out
   

def ms_cim_enumerate(classname, filter=''):
   """
   enum <CLSNAME> <KW>
   """
   if not CIM_SRV_IS_SET:
      set_cim_server()
   return exec_sms_cmd(_build_cmd('enum', classname, filter), parser=eMSLogItemList)

def ms_get_sizes_info(srcpool, parser=eMSSizeInfoItem):   
   """
   """
   if isinstance(srcpool, basestring):
      srcpoolobjid = srcpool
   else:
      srcpoolobjid = srcpool.ObjectId
   return exec_sms_cmd(_build_cmd_with_acc('getSizesInfo', srcpoolobjid), parser)

def ms_stg_grp(hardid_or_volname, type='hardwareid', parser=eMSStgGrpItem):
   """
   type = 'harwareid' or 'volumename'
   'harwareid' : StgGrpbyStorageHardwareID
   'volumename' : StgGrpbyVolumeName
   """
   if isinstance(hardid_or_volname, basestring):
      if type == 'hardwareid':
         cmd = 'StgGrpbyStorageHardwareID'
      else:
         cmd = 'StgGrpbyVolumeName'
   elif isinstance(hardid_or_volname, eMSLogItem):
      if type == 'hardwareid':
         cmd = 'StgGrpbyStorageHardwareID'
         hardid_or_volname = hardid_or_volname.StorageID
      else:
         cmd = 'StgGrpbyVolumeName'
         hardid_or_volname = hardid_or_volname.Name
   return exec_sms_cmd(_build_cmd(cmd, hardid_or_volname), parser)

def create_pool_storage_volume(srcpoolid, elementname, size, nsf='true', pkgrdg=1, datardg=1, paritylayout='', parser=cMSLogItem):
   """
   cpsv or createPoolStorageVolume <srcPool-objID><elementName><new-size-in-bytes>
   <nosinglepointoffailure(0=false,1=true)><packageredundancygoal>
   <dataredundancygoal><paritylayout> [[user name] [pwd|!]]
   """
   cmd = _build_cmd_with_acc('createPoolStorageVolume', srcpoolid, elementname, size, nsf, pkgrdg, datardg, paritylayout)
   return exec_sms_cmd(cmd, parser)

def ms_get_unmapped_hosts(system=None):
   """
   return hosts, clusters
   """
   rethosts = LogListBase('')
   retclusters = LogListBase('')
   hostlist = ms_enumerate("MSFT_SMStorageHardwareID", "all")
   hostmaplist = ms_enumerate("MSFT_SMStorageGroupToStorageHardwareID", "all")
   clustermaplist = ms_cim_enumerate('IBMTSDS_SystemSpecificCollectionToStorageHardwareID')
   
   clusters = ms_get_clusters(hostlist)
   for cluster in clusters:
      clusterhosts = ms_get_hosts_in_cluster(cluster, hostlist, clustermaplist)
      for host in clusterhosts:
         hostlist.remove(host)
      hostlist.remove(cluster)
   for host in hostlist:
      if not ms_is_host_mapped(host, hostmaplist):
         rethosts.append(host)
   for cluster in clusters:
      if not ms_is_host_mapped(cluster, hostmaplist):
         retclusters.append(cluster)
   return rethosts, retclusters

def ms_get_clusters(hosts=None, system=None):
   collectiolist = ms_cim_enumerate('IBMTSDS_SystemSpecificCollection')
   for collection in collectiolist:
      storageid = instanceid_to_storageid(collection.InstanceID)
   hostlist = hosts or ms_enumerate("MSFT_SMStorageHardwareID", "all")
   return hostlist.GetItemList('StorageID', storageid)

def ms_get_hosts_in_cluster(cluster, hosts=None, clustermaplist=None, system=None):
   retlist = LogListBase('')
   instid = get_attr_from_value(cluster.ObjectId)['InstanceID']
   collection = "IBMTSDS_SystemSpecificCollection.InstanceID=\"" + instid +"\""
   clustermaplist = clustermaplist or ms_cim_enumerate('IBMTSDS_SystemSpecificCollectionToStorageHardwareID')
   hoststocluster = clustermaplist.GetItemList('Collection', collection)
   hostlist = hosts or ms_enumerate("MSFT_SMStorageHardwareID", "all")
   for host in hoststocluster:
      storageid = instanceid_to_storageid(get_attr_from_value(host.Member)['InstanceID'])
      retlist.extend(hostlist.GetItemList('StorageID', storageid))
      
   return retlist

def ms_is_host_mapped(host, hostmaplist=None):
   hostmaplist = hostmaplist or ms_enumerate("MSFT_SMStorageGroupToStorageHardwareID", "all")
   if hostmaplist.Filter('Child', host.ObjectId):
      return True
   else:
      return False
   
def ms_get_host_ports(host, ibmhostlist=None):
   ibmhostlist = ibmhostlist or ms_cim_enumerate('IBMTSDS_StorageHardwareID')
   retlist = ibmhostlist.GetItemList('StorageID', host.StorageID)
   if retlist:
      ids = retlist[0].PortIDs
      if not ids.startswith('{') or not ids[1:-1]:
         return []
      return map(str.strip, ids[1:-1].split(','))
   return []

def _build_cmd(*args):
   return ' '.join([str(arg) for arg in args])

def _build_cmd_with_acc(*args):
   return _build_cmd(*args) + ' ' + config.get('CLI server', 'user') + ' ' + config.get('CLI server', 'passwd')

def cim_enumerate(classname, filter='', parser=eCIMLogItemList):
   """
   CIMlib.exec_cim_cmd('cli201 gi \'IBMTSDS_StorageSystem.CreationClassName="IBMTSDS_StorageSystem",Name="IBM.2810-6000095"\'')
   """
   return exec_cim_cmd('cimcli -n root/ibm -niq ei %s' % classname, parser)

def xiv_env_setup():
   """
   export XIVAGENT_HOME=/local/cim
   . $XIVAGENT_HOME/config/envConf
   """
   exec_cim_cmd("export XIVAGENT_HOME=/local/cim")
   exec_cim_cmd(". $XIVAGENT_HOME/config/envConf")
   
def ds_env_setup():
   """
   . /opt/IBM/dsagent/config/envConf
   """
   exec_cim_cmd(". /opt/IBM/dsagent/config/envConf")

def _build_cim_cmd(cmd, *args):
   pass

def objectid_to_path(value):
   """
   ObjectId=root/ibm:s9.186.96.203:5989;IBMTSDS_StorageSystem.CreationClassName=%'IBMTSDS_StorageSystem%',Name=%'IBM.2810-1300207%'
   """
   if value.startswith(config.get('CIM Agent', 'namespace')):
      return value.partition(';')[-1]
   else:
      raise RuntimeError('Format not supported in this version!')
   
def path_to_objectid(value, system=None):
   """
   ObjectId=root/ibm:s9.186.96.203:5989;IBMTSDS_StorageSystem.CreationClassName=%'IBMTSDS_StorageSystem%',Name=%'IBM.2810-1300207%'
   path= IBMTSDS_SystemSpecificCollectionToStorageHardwareID.Collection="root/ibm:IBMTSDS_SystemSpecificCollection.InstanceID=\"IBMTSDS:IBM.2810-1300207-100527\"",Member="root/ibm:IBMTSDS_StorageHardwareID.InstanceID=\"IBMTSDS:IBM.2810-1300207-100525\""
   Member = "root/ibm:IBMTSDS_StorageHardwareID.InstanceID=\"IBMTSDS:IBM.2810-1300207-100525\"";
   """
   system = system or ms_enumerate("MSFT_SMSystem")[0]
   prefix = system.ObjectId.split(';')[0]
   if value.startswith(config.get('CIM Agent', 'namespace')):
      return prefix + ';' + value.partition(';')[-1]
   else:
      return prefix + ';' + value
   
def instanceid_to_storageid(value):
   """
   "IBMTSDS:IBM.2810-1300207-100561"
   IBM.2810-1300207-100561
   """
   if value.startswith('IBMTSDS:'):
      return value.replace('IBMTSDS:', '')
   return value.partition(':')[-1] or value

def test_cimagent():
    connect_to_cimagent()
    #CIMAgent.set_prompt('[\w\~\@\:\-]+\$ ', True)
    print 'logged in'
    print CIMAgent.execute_command('ls', 'stdout')
    print CIMAgent.execute_command('ls')
    print '======='
    print exec_cim_cmd('ls')
    print '=========='
    exec_cim_cmd('cd /etc')
    exec_cim_cmd('ls')
    close_all_connections()
    
def test_winssh():
    connect_to_win()
    print '========= login ========='
    #MSClient.set_prompt('[A-Za-z]\:\\\\[\w \\\\\-\.]*>', True)
#    print MSClient.execute_command('dir')
#    MSClient.open_shell()
#    MSClient.read_until_prompt()
#    print '================'
#    print MSClient.execute_command('cd "c:\\Program Files\\Microsoft Storage Management Service"')
#    print MSClient.execute_command('dir')
#    
    exec_ms_cmd("dir")
    print '============'
    exec_ms_cmd("dir")
    print '============'
    
    exec_ms_cmd('cd "c:\\Program Files\\Microsoft Storage Management Service"')
    exec_ms_cmd("dir")
    exec_sms_cmd("e MSFT_SMEndpoint")
    close_all_connections()


if __name__ == '__main__':
   #test_cimagent()
   test_winssh()
   pass
   

